#include "LinkManager.hpp"
#include "ChessGame.hpp"
#include <cassert>

#define UNACTIVATE_ALL_CELLS()													\
		unsigned char											ii;				\
		unsigned char											jj;				\
		for (ii = 0 ; ii < 8 ; ii++)											\
		{																		\
			for (jj = 0 ; jj < 8 ; jj++)										\
			{																	\
				ChessGame::getInstance()->getView()->getChessboardView()		\
				->getCellWidget(Position(ii, jj))->setActivatedAsChoice(false);	\
			}																	\
		}


#define UNACTIVATE_ALL_PIECES_INIT()											\
		map<Piece*, PieceWidget*>::const_iterator						iiip;


#define UNACTIVATE_ALL_PIECES_FOR_PLAYER(COLOR)																		\
		UNACTIVATE_ALL_PIECES_INIT();																				\
		for (iiip = ChessGame::getInstance()->getView()->getPlayerView(COLOR)->getPieceWidgets().begin() ;			\
			iiip != ChessGame::getInstance()->getView()->getPlayerView(COLOR)->getPieceWidgets().end() ; iiip++)	\
		{	iiip->second->setActivatedAsChoice(false); iiip->second->setPixmapSelected(false);	}															


#define UNACTIVATE_ALL_PIECES_FOR_ALL_PLAYERS()																				\
		UNACTIVATE_ALL_PIECES_INIT();																						\
		for (iiip = ChessGame::getInstance()->getView()->getPlayerView(Chess::WHITE)->getPieceWidgets().begin() ;			\
			iiip != ChessGame::getInstance()->getView()->getPlayerView(Chess::WHITE)->getPieceWidgets().end() ; iiip++)		\
			{iiip->second->setActivatedAsChoice(false);	iiip->second->setActivatedAsSelect(false);							\
				iiip->second->setPixmapSelected(false);	}																	\
																															\
		for (iiip = ChessGame::getInstance()->getView()->getPlayerView(Chess::BLACK)->getPieceWidgets().begin() ;			\
				iiip != ChessGame::getInstance()->getView()->getPlayerView(Chess::BLACK)->getPieceWidgets().end() ; iiip++)	\
			{iiip->second->setActivatedAsChoice(false);	iiip->second->setActivatedAsSelect(false);							\
								iiip->second->setPixmapSelected(false);	}					


#define UNACTIVATE_ALL_CASTLING_BUTTONS()																						\
		ChessGame::getInstance()->getView()->getMainWin()->getCastlingButton(CastlingAction::KING_SIDE)->setEnabled(false);		\
		ChessGame::getInstance()->getView()->getMainWin()->getCastlingButton(CastlingAction::QUEEN_SIDE)->setEnabled(false);



LinkManager* LinkManager::instance = 0;

LinkManager*	LinkManager::getInstance()
{
	try
	{
		if (LinkManager::instance == 0)
			LinkManager::instance = new LinkManager();
			
		return (LinkManager::instance);
	}
	catch(Exception* e)
	{	throw e;	}
}

LinkManager::LinkManager()
{
	selected = 0;
	
	// connect castling buttons
	connect(ChessGame::getInstance()->getView()->getMainWin()->getCastlingButton(CastlingAction::KING_SIDE), SIGNAL(clicked()), this, SLOT(castlingSelect()));
	connect(ChessGame::getInstance()->getView()->getMainWin()->getCastlingButton(CastlingAction::QUEEN_SIDE), SIGNAL(clicked()), this, SLOT(castlingSelect()));
	connect(ChessGame::getInstance()->getView()->getMainWin()->getNewAction(), SIGNAL(triggered()), this, SLOT(new_()));
    connect(ChessGame::getInstance()->getView()->getMainWin()->getSaveAction(), SIGNAL(triggered()), this, SLOT(save()));
    connect(ChessGame::getInstance()->getView()->getMainWin()->getLoadAction(), SIGNAL(triggered()), this, SLOT(load()));
}


LinkManager::~LinkManager()
{
	cacodes.clear();
	pacodes.clear();
	pacells.clear();
	papieces.clear();
	bcactions.clear();
	selected = 0;
	
	UNACTIVATE_ALL_CELLS();
	UNACTIVATE_ALL_PIECES_FOR_ALL_PLAYERS();
}


void	LinkManager::update(const map<unsigned short, MoveAction*> & mactions,
							const map<CastlingAction::Type, CastlingAction*>& cactions,
							Chess::Color player)
{
	try
	{		
		MoveAction*												maction;
		QPushButton*											button;
		CellWidget*												w_cell;
		PieceWidget*											w_piece_src;
		PieceWidget*											w_piece_dst;
		unsigned short											hash_key;
		
		map<unsigned short, MoveAction*>::const_iterator			it;
		map<CastlingAction::Type, CastlingAction*>::const_iterator	is;
		
		// 0. destroy all previous data
		cacodes.clear();
		pacodes.clear();
		pacells.clear();
		papieces.clear();
		bcactions.clear();
		selected = 0;
		
		// 1. make everything unclickable
		
		// 1.1 make all cells unclickable
		UNACTIVATE_ALL_CELLS();
		
		// 1.2 make all pieces unclickable
		UNACTIVATE_ALL_PIECES_FOR_ALL_PLAYERS();
		
		// 1.3 make castling buttons unclickables
		UNACTIVATE_ALL_CASTLING_BUTTONS();
		
		// 2. build temporary data about move actions && make pieces clickables as select
		for (it = mactions.begin() ; it != mactions.end() ; it++)
		{
			maction = it->second;
			hash_key = it->first;
			
			w_cell = ChessGame::getInstance()->getView()->getChessboardView()->getCellWidget(maction->getPos2());
			w_piece_src = ChessGame::getInstance()->getView()->getPlayerView(player)->getPieceWidgets().find(Chess::getInstance()->getChessboard()->getCell(maction->getPos1())->getPiece())->second;
			w_piece_dst = (Chess::getInstance()->getChessboard()->getCell(maction->getPos2())->hasPiece())
							? ChessGame::getInstance()->getView()->getPlayerView(Chess::oppositeColor(player))->getPieceWidgets().find(Chess::getInstance()->getChessboard()->getCell(maction->getPos2())->getPiece())->second
							: 0;
			
			w_piece_src->setTransparent(false);				
			cacodes[w_cell][w_piece_src] = hash_key;
			
			if (w_piece_dst != 0)
				pacodes[w_piece_dst][w_piece_src] = hash_key;
				
			pacells[w_piece_src].push_back(w_cell);
			
			if (w_piece_dst != 0)
				papieces[w_piece_src].push_back(w_piece_dst);
				
			w_piece_src->setActivatedAsSelect();
		}
		
		// 3. build temporary data about castling actions && make castling buttons clickables
		for (is = cactions.begin() ; is != cactions.end() ; is++)
		{
			(button = ChessGame::getInstance()->getView()->getMainWin()->getCastlingButton(is->first))->setEnabled(true);
			bcactions[button] = is->second;
			
		}
	}
	catch(Exception* e)
	{
		throw e;
	}
}


// CONNECT CODE : void	Piece::setActivatedAsSelect(bool val = true);
void LinkManager::pieceSelect()
{
	try
	{
		PieceWidget*		pwidget = 0;
		
		assert ((pwidget = static_cast<PieceWidget*> (sender())) != 0);
		
		UNACTIVATE_ALL_PIECES_FOR_PLAYER(Chess::oppositeColor(pwidget->getPiece()->getColor()));
		UNACTIVATE_ALL_CELLS();
		
		// activates all pieces & cells choices !
		list<CellWidget*>::iterator it;
		list<PieceWidget*>::iterator ip;
		map<Piece*, PieceWidget*>::const_iterator is;
		
		for (it = pacells[pwidget].begin() ; it != pacells[pwidget].end() ; it++)
			(*it)->setActivatedAsChoice();
		
		for (ip = papieces[pwidget].begin() ; ip != papieces[pwidget].end() ; ip++)
			(*ip)->setActivatedAsChoice();
		
		// unhighlight pieces widgets for current player
		for (is = (ChessGame::getInstance()->getView()->getPlayerView(pwidget->getPiece()->getColor())->getPieceWidgets()).begin();
			is != (ChessGame::getInstance()->getView()->getPlayerView(pwidget->getPiece()->getColor())->getPieceWidgets()).end();
			is++)
			is->second->setPixmapSelected(false);
		
		selected = pwidget;
		selected->setPixmapSelected();
		ChessGame::getInstance()->getView()->getScene()->update();
		ChessGame::getInstance()->getView()->getMainWin()->getGraphicsView()->update();
		
	}
	catch(Exception* e)
	{	throw e;	}
}

// CONNECT CODE : void	Piece::setActivatedAsChoice(bool val = true);
void LinkManager::pieceChoice()
{
	try
	{
		PieceWidget*		pwidget = 0;
		unsigned short		hash_key;
		
		assert ((pwidget = static_cast<PieceWidget*> (sender())) != 0);
		UNACTIVATE_ALL_CELLS();
		UNACTIVATE_ALL_PIECES_FOR_ALL_PLAYERS();
		UNACTIVATE_ALL_CASTLING_BUTTONS();
		hash_key = pacodes[pwidget][selected];
		selected = 0;
		ChessGame::getInstance()->doMoveAction(hash_key);
	}
	catch(Exception* e)
	{	e->act();	}
}


// CONNECT CODE : void	Cell::setActivatedAsChoice(bool val = true);
void LinkManager::cellChoice()
{
	try
	{
		CellWidget*			cwidget = 0;
		unsigned short		hash_key;
		
		assert ((cwidget = static_cast<CellWidget*> (sender())) != 0);
		UNACTIVATE_ALL_CELLS();
		UNACTIVATE_ALL_PIECES_FOR_ALL_PLAYERS();
		UNACTIVATE_ALL_CASTLING_BUTTONS();
		hash_key = cacodes[cwidget][selected];
		selected = 0;
		ChessGame::getInstance()->doMoveAction(hash_key);
	}
	catch(Exception* e)
	{	e->act();	}	
}


void LinkManager::castlingSelect()
{
	try
	{
		QPushButton*	button = 0;
		
		assert((button = static_cast<QPushButton*> (sender())) != 0);
		
		UNACTIVATE_ALL_CELLS();
		UNACTIVATE_ALL_PIECES_FOR_ALL_PLAYERS();
		UNACTIVATE_ALL_CASTLING_BUTTONS();
		ChessGame::getInstance()->doAction(bcactions[button]);
	}
	catch(Exception* e)
	{	throw e;	}
}

bool LinkManager::warning_playing()
{
	try
	{
		QMessageBox msgBox;
		int			ret;
		
		msgBox.setText("The current game has not been finished !");		
		msgBox.setInformativeText("Do you want to save the current game ?");
		msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard
		| QMessageBox::Cancel);
		ret = msgBox.exec();
		
		switch(ret)
		{
			case QMessageBox::Save:
				this->save();
				break;
				
			case QMessageBox::Discard:
				break;
			
			case QMessageBox::Cancel:
				return (true);
			
			default:
				break;
		}
		
		return (false);
	}
	catch(Exception* e)
	{	throw e;	}
}

void LinkManager::new_()
{
	try
	{
		if (warning_playing())
			return;
			
		ChessGame::getInstance()->reset();
	}
	catch(Exception *e)
	{	throw e;	 }
}

void LinkManager::load()
{
	try
	{
		if (warning_playing())
			return;
		
		QString filename = QFileDialog::getOpenFileName( 
			ChessGame::getInstance()->getView()->getMainWin(), 
			QObject::tr("Load File"), 
			QDir::currentPath(), 
			QObject::tr("chkmate Save (*.sav)") );
		
		if (!filename.isNull())
			ChessGame::getInstance()->load(filename.toStdString());
    }
    catch(Exception* e)
    {	e->act(); }
}


void LinkManager::save()
{	
	try
	{
		QString filename = QFileDialog::getSaveFileName( 
			ChessGame::getInstance()->getView()->getMainWin(), 
			tr("Save File"), 
			QDir::currentPath(), 
			tr("chkmate Save (*.sav)") );
		
		
		string	str = filename.toStdString();
		size_t	res = str.find_last_of(".");
		
		if ((res == str.npos) || !(str.substr(res + 1) == string("sav")))
			str += string(".sav");
		
		if (!filename.isEmpty())
			ChessGame::getInstance()->save(str);
	}
	catch(Exception* e)
	{	e->act();	}
}

